package cn.iocoder.yudao.module.system.tcpserver.protocol2;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.zkdata.ZkDataDO;
import cn.iocoder.yudao.module.system.tcpserver.pojo.NewProtocolConstant;
import cn.iocoder.yudao.module.system.tcpserver.pojo.ZkPacketFrame;
import cn.iocoder.yudao.module.system.tcpserver.utils.ZkCrcUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 说明：处理器
 */
@ChannelHandler.Sharable
@Component
@Slf4j
public class NewProtocolServerHandler extends SimpleChannelInboundHandler<Object> {

    @Resource
    private NewZkDataProcessService zkDataService;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object obj)
            throws Exception {
        Channel incoming = ctx.channel();

        // 如果不是protobuf类型的数据
        if (!(obj instanceof ZkDataDO)) {
            log.info("NNNN 未知数据!" + obj);
            return;
        }
        ZkDataDO msg = (ZkDataDO) obj;
        try {

            log.info("NNNN Client->Server:{}-- oriData:{}", msg.getDataSender(), msg.getOriData());
            log.info("NNNN Client->Server:{}--msgJson:{}", msg.getDataSender(), JSONUtil.toJsonStr(msg));

            ZkPacketFrame packetFrame = zkDataService.process(msg);
            if (packetFrame != null && packetFrame.packet() != null) {
                ctx.writeAndFlush(packetFrame);


                //第一次连接维护 状态
                if (packetFrame.isFirstHeartData()) {
                    ScheduledFuture<?> future = ctx.executor().scheduleAtFixedRate(
                            () -> zkDataService.sendServerHeartbeat(ctx.channel(), msg.getDataSender()),
                            5, 5, TimeUnit.SECONDS // 5秒后首次发送，之后每5秒一次
                    );

                    if (NewHeartbeatServer.getClientMap().containsKey(msg.getDataSender())) {
                        NewHeartbeatServer.ChannelSchedule schedule = NewHeartbeatServer.getClientMap().remove(msg.getDataSender());
                        if (schedule != null && schedule.future != null) {
                            schedule.future.cancel(true);
                            log.info("NNNN 111心跳任务关闭：{}", msg.getDataSender());
                        }
                        if (schedule != null && schedule.channel != null) {
                            schedule.channel.close();
                            log.info("NNNN 111连接关闭：{}", msg.getDataSender());
                        }
                    }

                    NewHeartbeatServer.getClientMap().put(msg.getDataSender(),
                            new NewHeartbeatServer.ChannelSchedule(ctx.channel(), future)
                    );
                } else if (packetFrame.isUploadEndACK()) {
                    //下行请求响应 此时需要再发送数据
//                    String receiver = null;
//                    for (Map.Entry<String, NewHeartbeatServer.ChannelSchedule> stringChannelScheduleEntry : NewHeartbeatServer.getClientMap().entrySet()) {
//                        NewHeartbeatServer.ChannelSchedule value = stringChannelScheduleEntry.getValue();
//                        if (value.channel != null && value.channel == ctx.channel()) {
//                            receiver = stringChannelScheduleEntry.getKey();
//                            break;
//                        }
//                    }
                    String receiver = null;
                    if (Objects.equals(msg.getDataSender(), "1")) {
                        receiver = NewProtocolConstant.DEVICE01NO;
                    } else if (Objects.equals(msg.getDataSender(), "2")) {
                        receiver = NewProtocolConstant.DEVICE02NO;
                    }
                    if (receiver != null) {
                        msg.setDataSender(receiver);
                        ZkPacketFrame sendData = zkDataService.getSendData(msg);
                        if (sendData != null && sendData.packet() != null) {
                            ctx.writeAndFlush(sendData);
                        }

//                        List<ZkDataDO> dataToSend = zkDataService.getDataToSend(receiver);
//                        byte[] downloadBeginData = NewProtocolConstant.DOWNLOAD_BEGIN_DATA;
//
//                        byte highByte = (byte) ((dataToSend.size() >> 8) & 0xFF); // 高字节
//                        byte lowByte = (byte) (dataToSend.size() & 0xFF);         // 低字节
//                        downloadBeginData[8] = highByte;
//                        downloadBeginData[9] = lowByte;
//                        byte[] crc = ZkCrcUtils.getCrc(downloadBeginData);
//                        downloadBeginData[10] = crc[0];
//                        downloadBeginData[11] = crc[1];
//                        ctx.writeAndFlush(new ZkPacketFrame(downloadBeginData));
                    }
                }
            }


        } catch (Exception e) {
            log.error("channelRead0 error {}", obj);
            log.error("channelRead0 error", e);
        } finally {
            ReferenceCountUtil.release(obj);
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("NNNN 连接断开");
        // 客户端断开时清理资源
//        HeartbeatServer.getClientMap().values().removeIf(s -> s.channel == ctx.channel());
        for (String s : NewHeartbeatServer.getClientMap().keySet()) {
            if (NewHeartbeatServer.getClientMap().get(s) != null &&
                    NewHeartbeatServer.getClientMap().get(s).channel == ctx.channel()) {
                NewHeartbeatServer.ChannelSchedule schedule = NewHeartbeatServer.getClientMap().remove(s);
                if (schedule != null && schedule.future != null) {
                    schedule.future.cancel(true);
                }
                zkDataService.clientOffLine(s);
                log.info("NNNN 连接断开：{}", s);
            }
        }
        ctx.fireChannelInactive();
    }


}
